import Parser from "web-tree-sitter";
import { Node, PathMap } from "../entities/types";
import { TreeSitter } from "../../../../core/treesitter/TreeSitter";
import { BRANCH_STATEMENTS, FOR_STATE, STOP_STATEMENTS } from "../entities/const";
export class HarmonyBranchMapProcessor {
  public static async extract(code: string, language: string): Promise<PathMap[]> {
    const pathMap: PathMap[] = [];
    const tree = TreeSitter.getInstance().getTree(code, language);
    this.languageId = language;
    if (tree) {
      const branchNodes = await this.retrieveBranches([tree.rootNode], code);
      if (branchNodes.length) {
        for (let i = branchNodes.length - 1; i >= 0; i--) {
          const currCond = branchNodes[i];
          this.addPath(currCond, pathMap);
        }
      }
    }
    return pathMap;
  }

  private static languageId = '';

  private static addPath(currCond: Node, pathMap: PathMap[]): void {
    if (!currCond) {
      return;
    };
    const branchType = this.branchTextType(currCond);
    if (branchType === 'case') {
      this.setBranchPaths(currCond, true, pathMap);
    } else {
      this.setBranchPaths(currCond, true, pathMap);
      this.setBranchPaths(currCond, false, pathMap);
    }
  }

  private static setBranchPaths(currCond: Node, covered: boolean, pathMap: PathMap[]): void {
    if (!currCond) {
      return;
    };
    let needIn = false;
    let tail = currCond;
    let addCondition = '';
    let path = '';
    while (tail && tail.parentCondition) {
      let prevCond = tail.parentCondition;
      // 判断自己是不是else里的语句，自己是，父节点条件要加上false
      if (tail?.associatedParent?.isParent) {
        const { newAddCondition, newPath } = this.handleIsParent(needIn, addCondition, path, prevCond);
        addCondition = newAddCondition;
        path = newPath;
      } else if (tail?.associatedParent?.isElse) {
        if (needIn) {
          addCondition = this.handleHasReturn(addCondition, prevCond);
          continue;
        } else {
          const addrowbranchTestText = this.addLineNumber(prevCond.branchTestText,
            prevCond.node.startPosition.row);
          const branchType = this.branchTextType(prevCond);
          addCondition = this.notenterCondition(branchType, addrowbranchTestText, prevCond);
          path = addCondition + path;
        }
      } else {
        // 没有嵌套关系的if语句，看上一个if条件中是否有return，有的话条件要加上not
        const { newAddCondition, newPath } = this.handleIsNotParent(needIn, addCondition, path, prevCond);
        addCondition = newAddCondition;
        path = newPath;
      }
      tail = tail.parentCondition;
    }
    const addrowbranchTestText = this.addLineNumber(currCond.branchTestText,
      currCond.node.startPosition.row);
    const branchType = this.branchTextType(currCond);
    if (branchType === 'case') {
      path = path + this.enterCondition(branchType, addrowbranchTestText, currCond);
      pathMap.push({
        key: '待测试分支' + '进入' + currCond.branchTestText,
        value: path,
        lastRow: currCond.node.endPosition.row,
        gmockTemplateStrList: [],
        cppStubTemplateStrList: [],
        gmock_template_str: '',
        cpp_stub_template_str: ''
      });
    } else {
      path = path + (covered ? this.enterCondition(branchType, addrowbranchTestText, currCond) :
        this.notenterCondition(branchType, addrowbranchTestText, currCond));
      pathMap.push({
        key: '待测试分支' + (covered ? '进入' : '不进入') + currCond.branchTestText,
        value: path,
        lastRow: covered ? currCond.node.endPosition.row : currCond.node.startPosition.row,
        gmockTemplateStrList: [],
        cppStubTemplateStrList: [],
        gmock_template_str: '',
        cpp_stub_template_str: ''
      });
    }
  }

  private static handleIsNotParent(needIn: boolean, addCondition: string, path: string, prevCond: Node)
    : { newAddCondition: string; newPath: string } {
    const addrowbranchTestText = this.addLineNumber(prevCond.branchTestText,
      prevCond.node.startPosition.row);
    const branchType = this.branchTextType(prevCond);
    if (prevCond.elseReturn) {
      if (!prevCond.hasReturn) {
        addCondition = this.enterCondition(branchType, addrowbranchTestText, prevCond);
      }
      needIn = true;
      path = addCondition + path;
      return { newAddCondition: addCondition, newPath: path };
    } else if (prevCond.hasReturn) {
      addCondition = this.notenterCondition(branchType, addrowbranchTestText, prevCond);
      path = addCondition + path;
      return { newAddCondition: addCondition, newPath: path };
    }
    addCondition = this.enterCondition(branchType, addrowbranchTestText, prevCond);
    path = addCondition + path;
    return { newAddCondition: addCondition, newPath: path };
  }

  private static notenterCondition(branchType: string, addrowbranchTestText: string, prevCond: Node) {
    let notenterCondition = '';
    if (branchType === 'if') {
      notenterCondition = addrowbranchTestText + ' do not enter the if branch\n';
    }
    if (branchType === 'for') {
      notenterCondition = addrowbranchTestText + ' do not enter the for loop\n';
    }
    if (branchType === 'while') {
      notenterCondition = addrowbranchTestText + ' do not enter the while loop\n';
    }
    if (branchType === 'default') {
      notenterCondition = addrowbranchTestText + ' fail to match all the cases';
    }
    return notenterCondition;
  }

  private static handleHasReturn(addCondition: string, prevCond: Node): string {
    const addrowbranchTestText = this.addLineNumber(prevCond.branchTestText,
      prevCond.node.startPosition.row);
    const branchType = this.branchTextType(prevCond);
    if (!prevCond.hasReturn) {
      addCondition = this.enterCondition(branchType, addrowbranchTestText, prevCond);
    } else {
      addCondition = this.notenterCondition(branchType, addrowbranchTestText, prevCond);
    }
    return addCondition;
  }

  private static handleIsParent(needIn: boolean, addCondition: string, path: string, prevCond: Node)
    : { newAddCondition: string; newPath: string } {
    const addrowbranchTestText = this.addLineNumber(prevCond.branchTestText,
      prevCond.node.startPosition.row);
    const branchType = this.branchTextType(prevCond);
    if (needIn) {
      addCondition = this.notenterCondition(branchType, addrowbranchTestText, prevCond);
    } else {
      addCondition = this.enterCondition(branchType, addrowbranchTestText, prevCond);
      path = addCondition + path;
    }
    return { newAddCondition: addCondition, newPath: path };
  }

  public static addLineNumber(context: string, start: number = 0, offset: number = 10): string {
    const numberedLines = [];
    let row = start + offset;
    for (const line of context.split('\n')) {
      let newLine = `line ${row}:${line}`;
      numberedLines.push(newLine);
      row++;
    }
    const numberedText = numberedLines.join('\n');
    return numberedText;
  }

  private static enterCondition(branchType: string, addrowbranchTestText: string, prevCond: Node) {
    let enterCondition = '';
    if (branchType === 'if') {
      enterCondition = addrowbranchTestText + ' enter the if branch\n';
    }
    if (branchType === 'for') {
      enterCondition = addrowbranchTestText + ' enter the for loop\n';
    }
    if (branchType === 'while') {
      enterCondition = addrowbranchTestText + ' enter the while loop\n';
    }
    if (branchType === 'case') {
      enterCondition = addrowbranchTestText + ' match the case ' + prevCond.conditionText;
    }
    if (branchType === 'default') {
      enterCondition = addrowbranchTestText + ' match the default cases';
    }
    return enterCondition;
  }

  private static async retrieveBranches(funtionNode: Parser.SyntaxNode[], code: string): Promise<Node[]> {
    const nodeList: Node[] = [];
    const stack: Parser.SyntaxNode[] = [...funtionNode];
    while (stack.length > 0) {
      const currentNode = stack.pop();
      if (currentNode && BRANCH_STATEMENTS.includes(currentNode.type)) {
        if (currentNode.type === 'case_statement') {
          await this.buildCaseStateNode(currentNode, nodeList, code);
          continue;
        }
        if (currentNode.type === 'catch_clause') {
          await this.buildCatchStateNode(currentNode, nodeList, code);
          continue;
        }
        nodeList.push({
          node: currentNode,
          hasReturn: await this.setBranchReturnInfo(currentNode),
          elseReturn: await this.setBranchElseReturnInfo(currentNode),
          conditionText: this.getConditionClause(currentNode),
          branchTestText: this.getBranchTestText(currentNode, code),
          parentCondition: nodeList.length ? nodeList[nodeList.length - 1] : null,
          associatedParent: {
            isElse: false,
            isParent: false
          },
          text: currentNode.text,
          type: currentNode.type,
          id: currentNode.id,
        });
      }
      // 添加子节点
      if (currentNode) {
        for (let i = currentNode.childCount - 1; i >= 0; i--) {
          stack.push(currentNode.child(i) as Parser.SyntaxNode);
        }
      }
    }
    this.handleNodeParent(nodeList);
    return nodeList;
  }

  private static handleNodeParent(nodeList: Node[]): void {
    nodeList.forEach((node: Node, index: number) => {
      if (node.type === 'case_statement') {
        return;
      }
      const parentId = this.getParentNode(node);
      if (!parentId) {
        // 不存在嵌套条件
        this.findParentCondition(nodeList, node, index);
        return;
      }
      // 如果是根节点并且不是第一个子节点的
      if (parentId === nodeList[0].id && index !== 1) {
        return;
      }
      // 存在嵌套条件，刷新父节点
      const parentNode = nodeList.filter((item: Node) => item.id === parentId);
      if (parentNode.length) {
        node.parentCondition = parentNode[0];
        node.associatedParent = {
          isElse: this.isAnElse(node),
          isParent: true
        };
      }
    });
  }

  private static getParentNode(element: Node): number | string {
    // 拿到第一个符合条件的父节点
    let parentNode = element.node.parent;
    while (!BRANCH_STATEMENTS.includes(parentNode?.type || '') && parentNode) {
      parentNode = parentNode.parent;
    }
    return parentNode?.id || '';
  }

  private static findParentCondition(nodeList: Node[], currentNode: Node, index: number): void {
    // 节点不存在嵌套关系，寻找上一个兄弟节点作为父节点
    // 1、兄弟节点包含return语句：不进入，寻找上一个兄弟节点作为父节点
    // 2、兄弟节点不包含return语句：进入，节点不变
    if (index === 0) {
      return;
    }
    for (let i = index - 1; i >= 0; i--) {
      const prevNode = nodeList[i];
      // 找到上一个不存在嵌套条件的
      if (!prevNode.associatedParent.isParent) {
        prevNode.hasReturn ? currentNode.parentCondition = prevNode : '';
        return;
      }
    }
  }

  // 判断节点是否是else
  private static isAnElse(element: Node): boolean {
    const parentNode: Parser.SyntaxNode | null = element.node.parent;
    if (parentNode?.type === 'else_clause') {
      return true;
    } else if (parentNode?.type === 'compound_statement') {
      if (parentNode?.parent?.type === 'else_clause' || parentNode?.parent?.type === 'while_statement') {
        // 如果是包裹在大括号里面，那就在往上找一层,while的条件也要不成立才能进入下面的逻辑
        return true;
      }
    }
    return false;
  }

  // 获取条件表达式
  private static getConditionClause(node: Parser.SyntaxNode): string | null {
    const sameState = [
      'switch_statement',
      'if_statement',
      'while_statement',
      'case_statement'
    ];
    const children = node.children;
    if (sameState.includes(node.type)) {
      return children[1]?.text || null;
    } else if (node.type === 'for_statement') {
      for (let item of children) {
        if (item.type === 'binary_expression') {
          return `(${item.text})`;
        }
      }
    } else if (node.type === 'for_range_loop') {
      let clauseText = '';
      for (let i = 1; i < children.length; i++) {
        clauseText = clauseText + children[i].text;
        if (children[i]?.text.trim() === ')') {
          return clauseText;
        }
      }
    }
    return null;
  }

  // 获取被测条件
  private static getBranchTestText(node: Parser.SyntaxNode, code: string): string {
    const children = node.children;
    let expressionText = '';
    if (FOR_STATE.includes(node.type)) {
      for (let i = 0; i < children.length; i++) {
        if (children[i].text.trim() === ')') {
          expressionText = code.slice(node.startIndex - node.startPosition.column, children[i].endIndex);
          break;
        }
      }
    } else if (node.type === "case_statement" && children[0].text !== "default") {
      expressionText = code.slice(children[0].startIndex - children[0].startPosition.column,
        children[0].endIndex) + ' ' + children[1].text + ' ' + children[2].text;
    } else {
      expressionText = code.slice(children[0].startIndex - children[0].startPosition.column,
        children[0].endIndex) + ' ' + children[1].text;
    }
    return expressionText;
  }

  private static async setBranchElseReturnInfo(node: Parser.SyntaxNode): Promise<boolean> {
    // if语句中包含else
    const stack: Parser.SyntaxNode[] = [node];
    while (stack.length > 0) {
      const currentNode = stack.pop();
      if (currentNode?.type === 'else_clause' && await this.setBranchReturnInfo(currentNode)) {
        return true;
      }
      for (let i = (currentNode?.childCount as number) - 1; i >= 0; i--) {
        stack.push(currentNode?.child(i) as Parser.SyntaxNode);
      }
    }
    return false;
  }

  // 判断语句中是否有return,或者break可以终止进行的条件
  private static async setBranchReturnInfo(node: Parser.SyntaxNode): Promise<boolean> {
    // 去除语句中嵌套的子条件，只判断在最外层是否含有return
    const childBranchNode: string[] = TreeSitter.getInstance().getTargetNodeText(node, BRANCH_STATEMENTS);
    let parentText = node.text;
    if (childBranchNode.length > 1) {
      childBranchNode.shift();
      childBranchNode.forEach((childText: string) => {
        parentText = parentText?.replace(childText, '');
      });
    }
    const tree = TreeSitter.getInstance().getTree(parentText, this.languageId);
    if (!tree) {
      return false;
    }
    const returnList = TreeSitter.getInstance().getTargetNodeText(tree.rootNode, STOP_STATEMENTS);
    if (returnList.length) {
      return true;
    }
    return false;
  }

  private static async buildCaseStateNode(currentNode: Parser.SyntaxNode, nodeList: Node[], code: string): Promise<void> {
    // case语句，第一个父节点是compound_statement，第二个父节点是 switch_statement
    const switchState = currentNode?.parent?.parent;
    const switchCondition = this.getConditionClause(switchState as Parser.SyntaxNode);
    const caseCondition = this.getConditionClause(currentNode);
    const conditionText = (currentNode.children[0].text === 'default' ?
      'fail to match all the cases' : caseCondition);
    nodeList.push({
      node: currentNode,
      hasReturn: await this.setBranchReturnInfo(currentNode),
      elseReturn: false,
      conditionText,
      branchTestText: this.getBranchTestText(currentNode, code),
      parentCondition: nodeList.length ? nodeList[nodeList.length - 1] : null,
      associatedParent: {
        isElse: true,
        isParent: false
      },
      text: currentNode.text,
      type: currentNode.type,
      id: currentNode.id,
    });
  }

  private static async buildCatchStateNode(currentNode: Parser.SyntaxNode, nodeList: Node[], code: string): Promise<void> {
    const tryStateNode = currentNode?.parent;
    const tryCondition = tryStateNode?.children[0];
    const conditionText = 'catch:row:' + currentNode.startPosition.row + 'column:' + currentNode.startPosition.column;
    nodeList.push({
      node: currentNode,
      hasReturn: await this.setBranchReturnInfo(currentNode),
      elseReturn: false,
      conditionText,
      branchTestText: this.getBranchTestText(currentNode, code),
      parentCondition: nodeList.length ? nodeList[nodeList.length - 1] : null,
      associatedParent: {
        isElse: false,
        isParent: true
      },
      text: currentNode.text,
      type: currentNode.type,
      id: currentNode.id,
    });
  }

  private static branchTextType(currCond: Node): string {
    const regexif = /^if/i;
    const regexfor = /^for/i;
    const regexwhile = /^while/i;
    const regexcase = /^case/i;
    const regexdefault = /^default/i;
    const trimmedLine = currCond.branchTestText.trimStart();

    if (regexif.test(trimmedLine)) {
      return 'if';
    }
    else if (regexfor.test(trimmedLine)) {
      return 'for';
    }
    else if (regexwhile.test(trimmedLine)) {
      return 'while';
    }
    else if (regexcase.test(trimmedLine)) {
      return 'case';
    }
    else if (regexdefault.test(trimmedLine)) {
      return 'default';
    }
    return '';
  }
}